package org.batatasframework.support.database.values;

import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import org.batatasframework.support.database.spi.ResultWritable;
import org.batatasframework.support.database.sql.field.SelectFragment;
import org.batatasframework.support.format.OptionalFormat;
import org.batatasframework.support.type.OptionalMatchable;


public class OptionsValueHolder extends AbstractSingleValueHolder{

	private final OptionalFormat format;
	
	public OptionsValueHolder(SelectFragment field, String alias, OptionalFormat format) {
		super(field, alias);
		this.format = format;
	}

	@Override
	public FieldValue read(ResultSet rs) throws Exception {
		return new OptionsValue(rs.getLong(alias));
	}

	@Override
	public StatisticsTotal createStatistics() {
		return new OptionsStatistics();
	}
	
	class OptionsValue extends AbstractValue<Long>{
		
		public OptionsValue(Long value) {
			super(value);
		}

		@Override
		public Object targetValue() {
			return format.format(value);
		}
	}
	
	class OptionsStatistics implements StatisticsTotal{

		private List<OptionsStatisticsHead> statisticsHeads = new ArrayList<>();
		private OptionsStatisticsHead nullHead;
		
		public OptionsStatistics() {
			
			String nullValue = format.getNullName();
			
			if(format.isNullable() && !nullValue.isEmpty()){
				nullHead = new OptionsStatisticsHead(new NullOptionsMatch(nullValue));
			}
			
			for(OptionalMatchable om : format.getOptions()){
				statisticsHeads.add(new OptionsStatisticsHead(om));
			}
		}
		
		private class OptionsStatisticsValue implements StatisticsValue{
			private OptionsAutoIncrease nullCell;
			private List<OptionsAutoIncrease> values = new ArrayList<>();
			public OptionsStatisticsValue(){
				if(null != nullHead){
					nullCell = nullHead.newCell();
				}
				
				for(OptionsStatisticsHead item : statisticsHeads){
					values.add(item.newCell());
				}
			}
			
			@Override
			public void read(ResultSet rs) throws Exception {
				long val = rs.getLong(alias);
				boolean hadOptions = false;
				for(OptionsAutoIncrease it : values){
					hadOptions = it.increase(val) || hadOptions;
				}
				if(!hadOptions && null != nullCell){
					nullCell.increase(val);
				}
			}

			@Override
			public void writeTo(ResultWritable resultWriter) throws Exception {
				if(null != nullHead && nullHead.sumValue > 0) resultWriter.append(nullCell.getValue());
				for(OptionsAutoIncrease it : values){
					resultWriter.append(it.getValue());
				}
			}
			
		}
		
		@Override
		public StatisticsValue newValue() {
			return new OptionsStatisticsValue();
		}

		@Override
		public void writeTo(ResultWritable resultWriter) throws Exception {
			if(null != nullHead && nullHead.sumValue > 0) resultWriter.append(nullHead.sumValue);
			
			for(OptionsStatisticsHead item : statisticsHeads){
				resultWriter.append(item.sumValue);
			}
		}

		@Override
		public String[] getItemNames() {
			String[] names;
			int i = 0;
			if(null != nullHead && nullHead.sumValue > 0){
				names = new String[statisticsHeads.size() + 1];
				names[i++] = nullHead.match.getName();
			}else{
				names = new String[statisticsHeads.size()];
			}
			
			for(OptionsStatisticsHead item : statisticsHeads){
				names[i++] = item.match.getName();
			}
			return names;
		}
	}
}

interface OptionsAutoIncrease {
	int getValue();
	boolean increase(long optionValue);
}

class OptionsStatisticsHead{
	final OptionalMatchable match;
	int sumValue;
	
	public OptionsStatisticsHead(OptionalMatchable match){
		this.match = match;
	}
	
	public OptionsAutoIncrease newCell(){
		return new OptionsAutoIncrease(){
			private int value;
			
			@Override
			public int getValue() {
				return value;
			}

			@Override
			public boolean increase(long optionValue) {
				if(match.match(optionValue)){
					value ++;
					sumValue ++;
					return true;
				}
				return false;
			}
			
		};
	}
}

class NullOptionsMatch implements OptionalMatchable{
	private final String name;
	
	public NullOptionsMatch(String name){
		this.name = name;
	}
	
	@Override
	public boolean match(long optionValue) {
		return true;
	}

	@Override
	public String getName() {
		return name;
	}
}
